#ifndef __PROFILE_H__
#define __PROFILE_H__

#include "Singleton.h"

//#pragma warning( disable:4786 )  

#define PROFILE_ENABLE

#ifdef PROFILE_ENABLE
	#define Profile(g, s) ProfileInstance profile_instance(g, s)
	#define ProfileBegin(g, s) ProfileManager::instance()->profileBegin(g, s)
	#define ProfileEnd(g, s) ProfileManager::instance()->profileEnd(s)
#else
	#define Profile(g, s) 
	#define ProfileBegin(g, s) 
	#define ProfileEnd(g, s) 
#endif

struct SProfileSample
{
	bool isValid;
	int profileGroup;
	unsigned int profileInstances;
	int openProfiles;
	char szName[256];
	unsigned long startTime;
	float accumulatorTime;
	float childrenSampleTime;
	int numParents;
	SProfileSample() {
		isValid = false;
		profileGroup = 0;
		profileInstances = 0;
		openProfiles = 0;
		szName[0] = 0;
		startTime = 0;
		accumulatorTime = 0.f;
		childrenSampleTime = 0.f;
		numParents = 0;
	}
};

struct SProfileSampleHistory
{
	bool isValid;
	int profileGroup;
	char szName[256];
	float average;
	float min;
	float max;
	float percent;
	float exclusive;
	float exclusivePercent;
	SProfileSampleHistory() {
		isValid = false;
		profileGroup = 0;
		szName[0] = 0;
		average = 0.f;
		min = 0.f;
		max = 0.f;
		percent = 0.f;
		exclusive = 0.f;
		exclusivePercent = 0.f;
	}
};

class ProfileManager : public Singleton<ProfileManager>
{
	static const int kNumProfileSamples = 128;
	static const int kNumOutputBuffer = 4096;

	SProfileSample sSamples[kNumProfileSamples];
	SProfileSampleHistory sHistory[kNumProfileSamples];
	char sGroups[kNumProfileSamples][256];
	char szOutputBuffer[kNumOutputBuffer];
	int sOutputBufferSize;

	unsigned long sStartProfileTime;
	unsigned long sEndProfileTime;
	long long groupMask;
	float numericalFilter;
	bool enable;

private:
	int findGroupId(const char* name);
	bool findProfileSample(const char* name, SProfileSample** sample);
	bool findProfileSampleHistory(const char* name, SProfileSampleHistory** history);
	void putTextBuffer(const char* text);
	void clearTextBuffer();
	SProfileSampleHistory* storeProfileInHistory(int groupId, const char* name, float sampleTime, float percentTime, float exclusiveTime, float exclusivePercentTime);
	void resetAll();
	int getProfileSampleCount();
	SProfileSample* getProfileSample(int index);
	int getProfileSampleHistoryCount();
	SProfileSampleHistory* getProfileSampleHistory(int index);
	void enableGroup(const char* name);
	void disableGroup(const char* name);
	float toSeconds(unsigned long time);
	

public:
	ProfileManager();
	~ProfileManager();

	void profileBegin(const char* group, const char* name);
	void profileEnd(const char* name);
	void setEnable(bool b);
	bool isEnable();

	void dumpOutputToBuffer();
	const char* getOutputBuffer();
	void setNumericalFilter(float value);
	float getNumericalFilter() const;

	static unsigned long currentMicroSeconds();
};


class ProfileInstance
{
	const char* szName;
public:
	ProfileInstance(const char* group, const char* name) : szName(name) {
		static ProfileManager* sProfile = ProfileManager::newInstance();
		ProfileManager::instance()->profileBegin(group, szName);
	}
	~ProfileInstance() {
		ProfileManager::instance()->profileEnd(szName);
	}
};

#endif //__PROFILE_H__
